package com.example.sunming.redisLock;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import java.util.concurrent.TimeUnit;

/**
 * @description: redis分布式锁工具类
 * @date: 2020/12/11
 * @author: sunming
 */
@Slf4j
@Component
public class RedisLock {
    private static final String SPLIT = "_";

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    /**
     * 加锁解锁工具类
     * @param lockKey 加锁的key
     * @param uuid 线程的标志
     * @param timeout 超时时间
     * @param timeUnit 超时时间粒度
     * @return true：获取成功
     */
    public boolean lock(String lockKey, String uuid, long timeout, TimeUnit timeUnit) {
        // 根据key获取值
        String currentLock = stringRedisTemplate.opsForValue().get(lockKey);

        // 值为：uuid_时间
        String value = uuid + SPLIT + (timeUnit.toMillis(timeout) + System.currentTimeMillis());

        // 如果为空，则设置值
        if (StringUtils.isEmpty(currentLock)) {
            if (stringRedisTemplate.opsForValue().setIfAbsent(lockKey, value, timeout, timeUnit)) {
                // 对应setnx命令，可以成功设置,也就是key不存在，获得锁成功
                return true;
            } else {
                return false;
            }
        } else {
            // 可重入锁，如果是这个uuid持有的锁，则更新时间
            if (currentLock.startsWith(uuid)) {
                stringRedisTemplate.opsForValue().set(lockKey, value, timeout, timeUnit);
                return true;
            } else {
                return false;
            }
        }
    }

    /**
     * 释放锁
     *
     * @param lockKey 加锁的key
     * @param uuid 线程的标志
     */
    public void release(String lockKey, String uuid) {
        try {
            String currentValue = stringRedisTemplate.opsForValue().get(lockKey);

            if (!StringUtils.isEmpty(currentValue)) {

                String[] split = currentValue.split(SPLIT);
                String lockUuid = split[0];

                if (lockUuid.equals(uuid)) {
                    // 删除锁状态
                    stringRedisTemplate.opsForValue().getOperations().delete(lockKey);
                }
            }
        } catch (Exception e) {
            log.error("解锁异常, key: {}, uuid: {}", lockKey, uuid);
            log.error("", e);
        }
    }
}
